// 1.接口的兼容性
namespace a {
    // 只要传入的接口中含有定义接口就行
    interface Animal {}
    interface Person {name: string}
    function getName(animal: Animal) {}
    let p : Person = {name:'ikki'}
    getName(p)
}
// 2.基本类型的兼容性
namespace b {
    let a : string | number
    let str:string = '1'
    a = str
    let str2 : {
        toString():string
    }
    let str3 :string = 'ikki'
    str2 = str3
}
// 3.类的兼容性
namespace c {
    class Animal{name:string}
    class Bird extends Animal{swing:number}
    let a:Animal;
    a = new Bird();
    let b:Bird;
    //并不是父类兼容子类，子类不兼容父类
    b = new Animal();
}
// 4.函数的兼容性
// 4.1 比较参数
namespace d {
    // 可以少参数，不能多参数
    type sumFunc = (a:number,b:number)=>void;
    let sum1:sumFunc =  function f1(a:number,b:number):void{}
    let sum2:sumFunc = function f2(a:number):void{} //可以省略一个参数
    let sum3:sumFunc = function f3():void{} //可以省略二个参数
    let sum4:sumFunc = function f4(a:number,b:number,c:number){} //多一个参数可不行
}
// 4.2 比较返回值
namespace e {
    type GetPerson = ()=>{name:string,age:number};
    let getPerson:GetPerson = function g1(){  //返回值一样可以
        return {name:'ikki',age:10};
    }
    let getPerson2:GetPerson =function g2(){  //返回值多一个属性也可以
        return {name:'ikki',age:10,gender:'male'};
    }
    let getPerson3:GetPerson =function g3(){  //返回值少一个属性可不行
        return {name:'ikki'};
    }
}

// 5.函数的协变与逆变
// 协变（Covariant）：只在同一个方向；
// 逆变（Contravariant）：只在相反的方向；
// 双向协变（Bivariant）：包括同一个方向和不同方向；
// 不变（Invariant）：如果类型不完全相同，则它们是不兼容的。

// A ≼ B 意味着 A 是 B 的子类型。

// A → B 指的是以 A 为参数类型，以 B 为返回值类型的函数类型。
// x : A 意味着 x 的类型为 A

// 返回值类型是协变的，而参数类型是逆变的

// 返回值类型可以传子类,参数可以传父类
// 参数逆变父类 返回值协变子类 搀你父,返鞋子
namespace f {
    class Animal{}
    class Dog extends Animal{
        public name:string = 'Dog'
    }
    class BlackDog extends Dog {
        public age: number = 10
    }
    class WhiteDog extends Dog {
        public home: string = '北京'
    }
    let animal: Animal;
    let blackDog: BlackDog;
    let whiteDog: WhiteDog;
    type Callback = (dog: Dog)=>Dog;
    function exec(callback:Callback):void{
        callback(whiteDog);
    }
    //不行  callback(redDog);
    type ChildToChild = (blackDog: BlackDog) => BlackDog;
    const childToChild: ChildToChild = (blackDog: BlackDog): BlackDog => blackDog
    exec(childToChild);
    
    //也不行,理由同上
    type ChildToParent = (blackDog: BlackDog) => Animal;
    const childToParent: ChildToParent = (blackDog: BlackDog): Animal => animal
    exec(childToParent);
    
    //不行 因为有可能调用返回的Dog的方法
    type ParentToParent = (animal: Animal) => Animal;
    const parentToParent: ParentToParent = (animal: Animal): Animal => animal
    exec(parentToParent);
    
    //可以,所有的狗都是动物,返回的不管什么狗都是狗
    type ParentToChild = (animal: Animal) => BlackDog;
    const parentToChild: ParentToChild = (animal: Animal): BlackDog => blackDog
    exec(parentToChild);
    //(Animal → Greyhound) ≼ (Dog → Dog)
    //返回值类型很容易理解：黑狗是狗的子类。但参数类型则是相反的：动物是狗的父类！
}

// 6.泛型的兼容性
// 泛型在判断兼容性的时候会先判断具体的类型,然后再进行兼容性判断
namespace g {
    interface Empty<T>{}
    let x!:Empty<string>
    let y!:Empty<number>
    x= y 
}

// 7.枚举的兼容性
// 枚举类型与数字类型兼容，并且数字类型与枚举类型兼容
// 不同枚举类型之间是不兼容的
namespace h {
    enum C { RED,BLUE }
    let c : C
    c = C.RED
    c = 1
    let n:number = 1 
    n = C.BLUE
}